package com.android.readcard.utils;

import android.os.Handler;
import android.os.Message;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Iterator;

/**
 * Created by Admin on 2016/7/31.
 */
//安全模块与身份证数据通讯传输通道类
public class FrameTrans extends Thread {
    String m_strIP; //服务器IP
    int m_nPort;//指令通道的端口
    static FrameTrans m_FrameTrans;
    CardDeviceIO mCardDeviceIO; //设备接口类
    AccelerateCache mAccelerateCache; //加速器类
    boolean m_bUDP = true; //是否为UDP
    boolean m_bStop = true;//循环是否结束
    Handler mErrHandler = null;//发生错误后，处理错误的hangler

    @Override
    public void run() {
        // TODO Auto-generated method stub
        super.run();
        m_bStop = false;

        if (m_bUDP) {
            FrameTransUDP2BLENIO(m_strIP, m_nPort);
            //FrameTransUDP2BLE(m_strIP, m_nPort);
        }
        else
            FrameTransTCP2BLE(m_strIP, m_nPort);
        System.out.println("thread exit");
    }

    public static FrameTrans GetFrameTrans()
    {
        if (m_FrameTrans == null)
        {
            m_FrameTrans = new FrameTrans();
        }
        return m_FrameTrans;
    }

    public void SetAccelerateCache(AccelerateCache accelerateCache)
    {
        mAccelerateCache = accelerateCache;
    }

    //启动该传输通道
    public void StartTrans(CardDeviceIO cardDeviceIO, AccelerateCache accelerateCache, String strIP, int nPort, boolean bUDP, Handler errHandler)
    {
        System.out.println("Start Trans Command!");
        m_strIP = strIP;
        m_nPort = nPort;
        m_bUDP = bUDP;
        mErrHandler = errHandler;
        mCardDeviceIO = cardDeviceIO;
        mAccelerateCache = accelerateCache;
        start();
    }

    //停止该传输通道
    public void StopTrans()
    {
        System.out.println("Stop Trans Command!");
        m_bStop = true;
        try {
            join();
        }catch (InterruptedException e)
        {
        }
    }

    public boolean IsRuning()
    {
        return !m_bStop;
    }

    int SeekFinePack(byte[] data, int nDataLen)
    {
        if (nDataLen >= 2 && (data[0] == 'r' || data[0] == 'f') && data[1] == ' ')
        {
            int nIndex = -1;
            for (int i = 0; i < nDataLen; i++)
            {
                if (data[i] == '\n') {
                    nIndex = i;
                    break;
                }
            }
            if (nIndex > 0)
            {
                return nIndex+1;
            }
        }
        return -1;
    }

    //以TCP协议方式传输
    void FrameTransTCP2BLE(String strIP, int nPort) {
        byte[] recvBuf = new byte[4096];
        byte[] retData = new byte[4096];
        //BufferedInputStream ds = null;
        InputStream in = null;
        OutputStream out = null;
        Socket clientSocket = null;
        try {
            clientSocket = new Socket();
            //clientSocket.setKeepAlive(true);
            clientSocket.setTcpNoDelay(true);
            clientSocket.setSoTimeout(1);
            SocketAddress socketAddress = new InetSocketAddress(strIP, nPort);
            clientSocket.connect(socketAddress);
            in = clientSocket.getInputStream();
            //ds = new BufferedInputStream(clientSocket.getInputStream());
            out = clientSocket.getOutputStream();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            if (mErrHandler != null) {
                Message msg = mErrHandler.obtainMessage(0, e);
                msg.sendToTarget();
            }
            return;
        }
        System.out.println("TCP Connect" + strIP + ":" + nPort);
        for (; !m_bStop ; ) {
            try {
                int nRecvLen = 0;
                int nReadLen = 0;
                /*
                if (ds.available() > 0) {
                    nReadLen = ds.read(receiveBuf);
                }
                */
                nRecvLen = in.available();
                if (nRecvLen > 0) {
                    try {
                        for (int i = 0; i < nRecvLen; i++)
                        {
                            int nData = in.read();
                            if (nData >= 0)
                            {
                                recvBuf[i] = (byte)nData;
                                nReadLen = SeekFinePack(recvBuf, i + 1);
                                if (nReadLen > 0)
                                    break;
                            }
                            else
                                break;
                        }
                    } catch (SocketTimeoutException e) {
                        nReadLen = 0;
                    }
                }
                /*
                try {
                    nReadLen = in.read(receiveBuf);
                } catch (SocketTimeoutException e) {
                    nReadLen = 0;
                }
                */

                if (nReadLen > 0 ) {
                    System.out.println("TCP Rx:" + new String(recvBuf, 0, nReadLen));
                }

                int nRetDataLen = CardReadWriteTrans(recvBuf, nReadLen, retData);
                if (nRetDataLen >= 0) {
                    System.out.println("TCP Tx:" + new String(retData, 0, nRetDataLen));
                    out.write(retData, 0, nRetDataLen);
                    out.flush();
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                if (mErrHandler != null) {
                    Message msg = mErrHandler.obtainMessage(0, e);
                    msg.sendToTarget();
                }
                break;
            }
        }
        try {
            if (clientSocket != null){
                clientSocket.close();
            }
            clientSocket = null;
            System.out.println("Net exit");
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        m_bStop = true;
        System.out.println("Net exit");
    }

    //以UDP协议方式传输
    void FrameTransUDP2BLE(String strIP, int nPort)
    {
        DatagramSocket clientSocket = null;
        byte[] recvBuf = new byte[4096];
        byte[] retData = new byte[4096];
        DatagramPacket recvPacket = new DatagramPacket(recvBuf , recvBuf.length);
        try {
            clientSocket = new DatagramSocket();
            clientSocket.setSoTimeout(1);
            DatagramPacket sendPacket = new DatagramPacket("1".getBytes(), 1, InetAddress.getByName(strIP), nPort);
            clientSocket.send(sendPacket);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            if (mErrHandler != null) {
                Message msg = mErrHandler.obtainMessage(0, e);
                msg.sendToTarget();
            }
        }
        System.out.println("UDP Connect" + strIP + ":" + nPort);
        ///////////////////////////////////////
        for (;!m_bStop;) {
            try {
                int nReadLen = -1;
                try {
                    clientSocket.receive(recvPacket);
                    nReadLen = recvPacket.getLength();
                } catch (SocketTimeoutException e) {
                    nReadLen = -1;
                    //System.out.println("UDP RX Time out");
                    //return;
                }

                if (nReadLen >= 0)
                {
                    System.out.println("UDP Rx:" + new String(recvBuf, 0, nReadLen));
                }
                int nRetDataLen = CardReadWriteTrans(recvBuf, nReadLen, retData);
                if (nRetDataLen >= 0) {
                    System.out.println("UDP Tx:" + new String(retData, 0, nRetDataLen));
                    DatagramPacket sendPacket = new DatagramPacket(retData, nRetDataLen, InetAddress.getByName(strIP), nPort);
                    clientSocket.send(sendPacket);
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                if (mErrHandler != null) {
                    Message msg = mErrHandler.obtainMessage(0, e);
                    msg.sendToTarget();
                }
                break;
            }
        }
        if (clientSocket != null)
            clientSocket.close();
        clientSocket = null;
        m_bStop = true;
        System.out.println("Net exit");
    }

    //以UDP NIO方式传输
    void FrameTransUDP2BLENIO(String strIP, int nPort) {
        byte[] recvBuf = new byte[4096];
        byte[] retData = new byte[4096];
        DatagramChannel channel = null;
        Selector selector = null;
        try
        {
            // 开启一个通道
            channel = DatagramChannel.open();
            channel.configureBlocking(false);
            SocketAddress sa = new InetSocketAddress(strIP, nPort);
            channel.connect(sa);
            ////////////////////////////////////
            selector = Selector.open();
            channel.register(selector, SelectionKey.OP_READ);
            channel.write(Charset.defaultCharset().encode("1"));
        } catch (Exception e)
        {
            e.printStackTrace();
            if (mErrHandler != null) {
                Message msg = mErrHandler.obtainMessage(0, e);
                msg.sendToTarget();
            }
        }
        System.out.println("UDP NIO Connect" + strIP + ":" + nPort);
        ////////////////////////////////////
        for(;!m_bStop;)
        {
            try {
                int nReadLen = ReadUDPSocketNIO(selector, recvBuf);
                if (nReadLen >= 0)
                {
                    System.out.println("UDP Rx:" + new String(recvBuf, 0, nReadLen));
                }

                int nRetDataLen = CardReadWriteTrans(recvBuf, nReadLen, retData);
                if (nRetDataLen >= 0) {
                    System.out.println("UDP Tx:" + new String(retData, 0, nRetDataLen));
                    DatagramPacket sendPacket = new DatagramPacket(retData, nRetDataLen, InetAddress.getByName(strIP), nPort);
                    channel.write(ByteBuffer.wrap(retData, 0, nRetDataLen));
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                if (mErrHandler != null) {
                    Message msg = mErrHandler.obtainMessage(0, e);
                    msg.sendToTarget();
                }
                break;
            }
        }
        try {
            if (channel != null)
                channel.close();
            if (selector != null)
                selector.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        channel = null;
        selector = null;
        m_bStop = true;
        System.out.println("Net exit");
    }

    int ReadUDPSocketNIO(Selector selector, byte[] buff) throws IOException
    {
        ByteBuffer byteBuffer = ByteBuffer.allocate(256);
        // 进行选择
        int n = selector.select(1);
        if (n > 0) {
            // 获取以选择的键的集合
            Iterator iterator = selector.selectedKeys().iterator();

            while (iterator.hasNext()) {
                SelectionKey key = (SelectionKey) iterator.next();

                // 必须手动删除
                iterator.remove();

                if (key.isReadable()) {
                    DatagramChannel datagramChannel = (DatagramChannel) key
                            .channel();

                    int nRedLen = datagramChannel.read(byteBuffer) ;
                    if (buff.length < nRedLen)
                        nRedLen = buff.length;
                    System.arraycopy(byteBuffer.array(), 0, buff, 0, nRedLen);
                    byteBuffer.flip();
                    byteBuffer.clear();
                    return nRedLen;
                }
            }
        }
        return -1;
    }

    /*
    //数据传输
    int CardReadWriteTrans(byte[] send, int nSendLen, byte[] recv) throws IOException
    {
        byte[] Query = null;
        Query = CommandItem.QueryStrigToBytes(send, nSendLen);
        /////////////////////////////////////////////////
        //System.out.println("send:"+HexUtil.bytesToHexString(Query));
        byte[] Ret = null;
        {
            Ret = mAccelerateCache.InEtherPack(Query);
            //Ret = mCardDeviceIO.DeviceIOPort(Query, 0);
        }
        if (Ret != null)
            Ret = CommandItem.ResponseBytesToString(Ret, Ret.length);
        if (Ret != null) {
            System.arraycopy(Ret, 0, recv, 0, Math.min(recv.length, Ret.length));
            return Ret.length;
        }
        return -1;
    }
    */

    int CardReadWriteTrans(byte[] send, int nSendLen, byte[] recv) throws IOException
    {
        byte[] query = null;
        if (nSendLen > 0)
        {
            if (send.length == nSendLen)
                query = send;
            else
                query = Arrays.copyOf(send, nSendLen);
        }
        /////////////////////////////////////////////////
        byte[] Ret = null;
        if (mAccelerateCache != null){
            Ret = mAccelerateCache.InEtherPack(query);
        }
        //////////////////////////////////////////////////////
        if (Ret != null) {
            System.arraycopy(Ret, 0, recv, 0, Math.min(recv.length, Ret.length));
            return Ret.length;
        }
        return -1;
    }
}
